﻿using System;
using System.IO;
using System.Net;
using System.Reflection;
using System.Collections;
using System.Net.Security;
using System.Security;
using System.Security.Cryptography.X509Certificates;

using Thinktecture.Tools.Web.Services.CodeGeneration;
using Thinktecture.Tools.Web.Services.CodeGeneration.Driver;
using Thinktecture.Tools.Web.Services.Wscf.Environment;

namespace Thinktecture.Tools.Web.Services.WscfCommandLine
{
    class Program
    {
        private static string GetThinktectureLibraryVersion()
        {
        	Assembly ttAssembly = typeof(WebServiceCodeGenerator).Assembly;
           return ttAssembly.GetName().Version.ToString();
       }

        private void PrintBanner()
        {
			// skip the banner if the user said to 
			// skip or if we already printed it.
			if( ( null != arguments 
					&&  true == arguments.SkipBanner )
				|| true == bannerPrinted  )
			{
				return;
			}
			else
			{
				Console.WriteLine( "thinktecture (R) Contract-First Web Services Code Generation Utility" );
				Console.WriteLine( string.Format( "[thinktecture Contract-First Libraries, Version {0}]", GetThinktectureLibraryVersion() ) );
				Console.WriteLine( string.Format( "[Microsoft (R) .NET Framework, Version {0}]", typeof(object).Assembly.ImageRuntimeVersion ) );
				Console.WriteLine( string.Format( "Copyright (C) thinktecture, with friendly help from Christoph Schittko. All rights reserved." ) );
				Console.WriteLine( "" );
				bannerPrinted = true;
			}
        }

		private static bool CheckHelp( string[] args )
		{
			bool needHelp = false;
			if( (1 == args.Length) && ((args[0].Length == 1 || args[0].Length == 2) && args[0].EndsWith("?")) )
			{
				needHelp = true;
			}
			return needHelp;
		}

		static void Main(string[] args)
		{
			new Program().Run(args);
		}

		private ContractFirstArguments arguments = null;
		private bool bannerPrinted = false;

		public void Run( string[] args )
		{
			if ( false == CheckHelp(args) )
			{
				OptionsTranslator translator = new OptionsTranslator();
				try
				{
					arguments = translator.GetArguments( args );
					PrintBanner();

					WebServicesCodeGenerationOptions options = translator.GetOptions( arguments );
					
					// CSC 11/18/2004
					// Write output files myself
					// CSC 11/25/2005
					// service or stub either or again
                    if (arguments.DataContract)
                    {                        
                        // CSC 11/24/2004
                        // Server code generation independent of the 
                        // proxy code generation
                        WebServiceCodeGenerator cgentor = new WebServiceCodeGenerator(arguments.CodeLanguage,
                                                                                      arguments.DataContractOutput,
                                                                                      arguments.GenerateProperties,
                                                                                      arguments.GenerateSerializableTypes,
                                                                                      arguments.GenerateCollections,
                                                                                      arguments.GenericList,
                                                                                      arguments.DataBinding,
                                                                                      arguments.Ordered,
                                                                                      arguments.AdjustCasing,
                                                                                      arguments.NoSpecifiedHandling,
                                                                                      arguments.GenerateMultipleFiles,
                                                                                      arguments.RemoveDefaultValueAttribute);
                        string[] xsdfiles = {Path.GetFullPath(arguments.WsdlLocation)};
                        SourceFiles sfs = cgentor.GenerateDataContracts(xsdfiles, arguments.DataContractNamespace);
                        WriteCodeGenWarnings(cgentor.Warnings);
                        new ResultFileWriter(true, arguments.Overwrite).WriteDataContract(sfs);
                    }
					else if ( false == arguments.GenerateServerCode )
					{
                        // CSC 11/16/2004
                        // Set WSDL content
                        options.WsdlContents = ReadWSDL(arguments.WsdlLocation);
					
                        // BDS 01/19/2006
                        // Added some validation code to make sure the file name and the 
                        // namespace name are good.
                        if (!string.IsNullOrEmpty(arguments.ProxyFileName) &&
                            !ValidationHelper.IsWindowsFileName(arguments.ProxyFileName))
                        {
                            Console.WriteLine("Invalid argument. Proxy file name should be a valid file name.");
                            return;
                        }

                        if (!string.IsNullOrEmpty(arguments.Namespace) &&
                            !ValidationHelper.IsDotNetNamespace(arguments.Namespace))
                        {
                            Console.WriteLine("Invalid argument. Namespace name should be a valid .Net namespace.");
                            return;
                        }

						// CSC 11/24/2004
						// Server code generation independent of the 
						// proxy code generation
                        WebServiceCodeGenerator cgentor = new WebServiceCodeGenerator( options, arguments.AssemblyName );
						SourceFiles sfs = cgentor.GenerateProxyCode( arguments.Namespace );
                        WriteCodeGenWarnings(cgentor.Warnings);
                        new ResultFileWriter(true, arguments.Overwrite).WriteProxy(
							sfs, arguments );                        
					}
					else
					{
                        // CSC 11/16/2004
                        // Set WSDL content
                        options.WsdlContents = ReadWSDL(arguments.WsdlLocation);
					
                        // BDS 01/19/2006
                        // Added some validation code to make sure the file name and the 
                        // namespace name are good.
                        if (!string.IsNullOrEmpty(arguments.ServiceStubFileName) &&
                            !ValidationHelper.IsWindowsFileName(arguments.ServiceStubFileName))
                        {
                            Console.WriteLine("Invalid argument. Stub file name should be a valid file name.");
                            return;
                        }

                        if (!string.IsNullOrEmpty(arguments.Namespace) &&
                            !ValidationHelper.IsDotNetNamespace(arguments.Namespace))
                        {
                            Console.WriteLine("Invalid argument. Namespace name should be a valid .Net namespace.");
                            return;
                        }

                        WebServiceCodeGenerator cgentor = new WebServiceCodeGenerator(options, arguments.AssemblyName);
                        SourceFiles sfs = cgentor.GenerateStubCode( arguments.Namespace );
                        WriteCodeGenWarnings(cgentor.Warnings);
                        new ResultFileWriter(true, arguments.Overwrite).WriteSerivce(sfs, arguments );                        
					}

					// CSC 11/19/2004
					// write warnings for soap message access and validation
					// CSC 11/25/2004
					// changed signature
					WriteWarnings(arguments, translator.Warnings);

				}
				catch ( Exception ex )
				{
					PrintBanner();
					WriteError( "Unable to generate source files from contract:" );
#if DEBUG
					WriteError( ex.ToString() );
#else
					WriteError( ex.Message );
#endif
					WriteError("");
					WriteError("Run wscf /? for usage instructions.");
				}
			}
			else
			{
				PrintBanner();
				PrintUsage();
			}            
		}

		private static void WriteWarnings(ContractFirstArguments args, string warnings )
		{
			// TODO: Consolidate these warnings with warnings from the
			// translator

			// CSC 11/25/2004
			// Write warnings from the translator
			System.Console.WriteLine( warnings );

			// CSC 11/20/2004
			// Only write soap message warning for client code
			if (true == args.EnableSoapMessagesAccess
				&& false == args.GenerateServerCode)
			{
				WriteWarning("Soap message access settings were added to the .config file. Be sure to deploy the assembly Thinktecture.Tools.Web.Services.Extensions.Messages.dll to the application directory." );
			}
				// CSC 11/20/2004
				// Write warning that /ma is ignored
			else if( true == args.GenerateServerCode
				&& true == args.EnableSoapMessagesAccess )
			{
				WriteWarning("Soap message access option ignored for service code generation");
			}

			if (true == args.EnableValidation)
			{
				WriteWarning("Message validation settings were added to the .config file. Be sure to deploy the assembly Thinktecture.Tools.Web.Services.Extensions.Validation.dll to /bin directory of your web application." );
			}
		}

        // 01/05/2006 BDS: Added this helper function to write the code generation warnings to the 
        // console.
        private static void WriteCodeGenWarnings(ArrayList warnings)
        {
            if (warnings != null)
            {
                if (warnings.Count > 0)
                {
                    bool header = false;
                    
                    foreach (string warning in warnings)
                    {
                        if (!header)
                        {
                            Console.WriteLine(string.Format("WARNING(s): {0}", warning));
                            header = true;
                        }
                        else
                        {
                            Console.WriteLine(warning);
                        }
                    }

                    Console.WriteLine("");
                }
            }
        }

		private static void WriteWarning(string text)
		{
			Console.WriteLine( "WARNING: " + text );
		}

		private static void WriteError(string text)
		{
			Console.WriteLine( text );
		}
		private static void PrintUsage()
		{
			Console.WriteLine( "" );
			Console.WriteLine( "Usage: " );
			Console.WriteLine( OptionsTranslator.GetUsage() );
		}

		private static string ReadWSDL( string location )
		{
			TextReader reader;
			string wsdl;
			Console.WriteLine( "Reading WSDL from: {0}", location);
			try
			{
				Uri wsdlUri = new Uri(location);
				if( true == wsdlUri.IsFile )
				{
					reader = new StreamReader(location);
				}
				else
				{
                    ServicePointManager.ServerCertificateValidationCallback =
                        new RemoteCertificateValidationCallback(CertValidation);

					reader = new StreamReader( 
						WebRequest.Create(
						wsdlUri).GetResponse().GetResponseStream() );
				}
			}
			catch( UriFormatException )
			{
				reader = new StreamReader(location);
			}
			
			wsdl = reader.ReadToEnd();
			reader.Close();
			return wsdl;
		}

        public static bool CertValidation(object sender, X509Certificate certificate, 
            X509Chain chain, SslPolicyErrors errors)
        {
            return true;
        }
	}
}
